package cn.flying.cloud.gateway.route;

import javax.annotation.Resource;

import java.net.URI;
import java.util.List;
import java.util.Map;

import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import reactor.core.publisher.Mono;

import cn.flying.cloud.gateway.route.model.GatewayRoute;
import cn.flying.cloud.gateway.route.service.GatewayRouteService;

/**
 * @author: admin
 * @date: 2024年05月09日 10:28
 * @version: 1.0
 */
@Component
public class GatewayRefreshRouteHandler implements ApplicationEventPublisherAware, CommandLineRunner {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private GatewayRouteService gatewayRouteService;
    @Resource
    private RedisRouteDefinitionRepository redisRouteDefinitionRepository;

    private ApplicationEventPublisher applicationEventPublisher;


    /**
     * @param applicationEventPublisher event publisher to be used by this object
     */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }


    /**
     * Callback used to run the bean.
     *
     * @param args incoming main method arguments
     * @throws Exception on error
     */
    @Override
    public void run(String... args) throws Exception {
        loadRouteDefinitions();
    }

    /**
     * 加载所有的路由配置
     */
    public void loadRouteDefinitions() {
        logger.info("--------加载所有的网关路由配置-------");
        List<GatewayRoute> list = gatewayRouteService.findAll();

        //先清除原来的路由配置，重新加载
        redisRouteDefinitionRepository.refresh();

        list.stream().forEach(route -> {
            RouteDefinition definition = new RouteDefinition();
            definition.setId(route.getRouteId());
            List<PredicateDefinition> predicates = JSONObject.parseObject(route.getPredicates(), new TypeReference<List<PredicateDefinition>>() {
            });
            definition.setPredicates(CollectionUtils.isEmpty(predicates) ? Lists.newArrayList() : predicates);
            List<FilterDefinition> filters = JSONObject.parseObject(route.getFilters(), new TypeReference<List<FilterDefinition>>() {
            });
            definition.setFilters(CollectionUtils.isEmpty(filters) ? Lists.newArrayList() : filters);
            definition.setUri(URI.create(route.getUri()));
            definition.setOrder(route.getSeq());
            definition.setMetadata(JSONObject.parseObject(route.getMetadata(), Map.class));

            redisRouteDefinitionRepository.save(Mono.just(definition)).subscribe();
        });

        this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
        logger.info("--------所有的网关路由配置加载完毕-------");
    }

    /**
     * 刷新路由配置
     */
    public void refreshRoutes() {
        loadRouteDefinitions();
    }
}
